/*
 * $AIST_Release: 0.9.0 $
 * Copyright 2011 Information Technology Research Institute, National
 * Institute of Advanced Industrial Science and Technology
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package aggr

import com.google.protobuf.Descriptors._
import com.google.protobuf.DescriptorProtos._
import com.google.protobuf.Descriptors.FieldDescriptor.Type._
import com.google.protobuf.DescriptorProtos.FieldDescriptorProto.Type._
import scala.collection.JavaConversions._
import util.PBUtils

/**
 * Module for types.
 */
object SerializedTypes {

  /**
   * Type of serialization.
   */
  sealed abstract class SerializedType(val descType: FieldDescriptor.Type, val protoType: FieldDescriptorProto.Type) {
    override def toString() = descType.toString.toLowerCase
  }

  case object BoolType                            extends SerializedType(BOOL,     TYPE_BOOL)
  case object FloatType                           extends SerializedType(FLOAT,    TYPE_FLOAT)
  case object DoubleType                          extends SerializedType(DOUBLE,   TYPE_DOUBLE)
  case object Fixed32Type                         extends SerializedType(FIXED32,  TYPE_FIXED32)
  case object Fixed64Type                         extends SerializedType(FIXED64,  TYPE_FIXED64)
  case object SFixed32Type                        extends SerializedType(SFIXED32, TYPE_SFIXED32)
  case object SFixed64Type                        extends SerializedType(SFIXED64, TYPE_SFIXED64)
  case object Int32Type                           extends SerializedType(INT32,    TYPE_INT32)
  case object Int64Type                           extends SerializedType(INT64,    TYPE_INT64)
  case object SInt32Type                          extends SerializedType(SINT32,   TYPE_SINT32)
  case object SInt64Type                          extends SerializedType(SINT64,   TYPE_SINT64)
  case object UInt32Type                          extends SerializedType(UINT32,   TYPE_UINT32)
  case object UInt64Type                          extends SerializedType(UINT64,   TYPE_UINT64)
  case object StringType                          extends SerializedType(STRING,   TYPE_STRING)
  case object BytesType                           extends SerializedType(BYTES,    TYPE_BYTES)
  case class EnumType(descriptor: EnumDescriptor) extends SerializedType(ENUM,     TYPE_ENUM) {
    override def toString() = descriptor.getName()
  }
  case class MessageType(descriptor: Descriptor)  extends SerializedType(MESSAGE,  TYPE_MESSAGE) {
    override def toString() = descriptor.getName()
  }
  val EmptyType = MessageType(pbdata.Tables.Empty.getDescriptor)

  private val basicTypes = List(
    BoolType, FloatType, DoubleType, Fixed32Type, Fixed64Type, SFixed32Type,
    SFixed64Type, Int32Type, Int64Type, SInt32Type, SInt64Type, UInt32Type, 
    UInt64Type, UInt32Type, StringType, BytesType, EmptyType
  )

  /**
   * Get "SerializedType" from "FieldDescriptor".
   */
  def getType(fld: FieldDescriptor): SerializedType = {
    fld.getType match {
      case ENUM     => EnumType(fld.getEnumType)
      case MESSAGE  => MessageType(fld.getMessageType)
      case fldType  => basicTypes.find(x => x.descType == fldType).get
    }
  }

  /**
   * Makes name and SerializedType map.
   */
  def makeSTypeMap(fds: List[FileDescriptor]): Map[String, SerializedType] = {
    val bm   = basicTypes.map{x => (x.toString, x) }
    val msgs = fds.flatMap(PBUtils.getMessages).map { x => (x.getName, MessageType(x)) }
    val enums = fds.flatMap(PBUtils.getEnums).map { x => (x.getName, EnumType(x)) }

    return Map() ++ bm ++ msgs ++ enums
  }
}
